package io.kiki.stack.http.feign.benchmark;

import feign.Target.HardCodedTarget;
import org.openjdk.jmh.annotations.*;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Measurement(iterations = 5, time = 1)
@Warmup(iterations = 10, time = 1)
@Fork(3)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class WhatShouldWeCacheBenchmarks {

    private Contract feignContract;
    private Contract cachedContact;
    private Client fakeClient;
    private Feign cachedFakeFeign;
    private FeignTestInterface cachedFakeApi;

    @Setup
    public void setup() {
        feignContract = new Contract.Default();
        cachedContact = new Contract() {
            private final List<MethodMetadata> cached = new Default().parseAndValidateMetadata(FeignTestInterface.class);

            public List<MethodMetadata> parseAndValidateMetadata(Class<?> declaring) {
                return cached;
            }
        };
        fakeClient = (request, options) -> {
            Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
            return Response.builder().body((byte[]) null).status(200).headers(headers).reason("ok").request(request).build();
        };
        cachedFakeFeign = Feign.builder().client(fakeClient).build();
        cachedFakeApi = cachedFakeFeign.newInstance(new HardCodedTarget<FeignTestInterface>(FeignTestInterface.class, "http://localhost"));
    }

    /**
     * How fast is parsing an api interface?
     */
    @Benchmark
    public List<MethodMetadata> parseFeignContract() {
        return feignContract.parseAndValidateMetadata(FeignTestInterface.class);
    }

    /**
     * How fast is creating a feign instance for each http request, without considering network?
     */
    @Benchmark
    public Response buildAndQuery_fake() {
        return Feign.builder().client(fakeClient).target(FeignTestInterface.class, "http://localhost").query();
    }

    /**
     * How fast is creating a feign instance for each http request, without considering network, and
     * without re-parsing the annotated http api?
     */
    @Benchmark
    public Response buildAndQuery_fake_cachedContract() {
        return Feign.builder().contract(cachedContact).client(fakeClient).target(FeignTestInterface.class, "http://localhost").query();
    }

    /**
     * How fast re-parsing the annotated http api for each http request, without considering network?
     */
    @Benchmark
    public Response buildAndQuery_fake_cachedFeign() {
        return cachedFakeFeign.newInstance(new HardCodedTarget<FeignTestInterface>(FeignTestInterface.class, "http://localhost")).query();
    }

    /**
     * How fast is our advice to use a cached api for each http request, without considering network?
     */
    @Benchmark
    public Response buildAndQuery_fake_cachedApi() {
        return cachedFakeApi.query();
    }
}
